"use strict";

import findPath from "./dijkstra";
//findPath = require('./dijkstra'),
import preprocess from "./preprocessor";
import compactor from "./compactor";
//roundCoord = require('./round-coord');

export default PathFinder;

function PathFinder(graph, options) {
    options = options || { vertices: [] };

    if (!graph.compactedVertices) {
        graph = preprocess(graph, options);
    }

    this._graph = graph;
    this._keyFn =
        options.keyFn ||
        function(c) {
            return c.join(",");
        };
    this._precision = options.precision || 1e-5;
    this._options = options;

    if (
        Object.keys(this._graph.compactedVertices).filter(function(k) {
            return k !== "edgeData";
        }).length === 0
    ) {
        throw new Error("Compacted graph contains no forks (topology has no intersections).");
    }
}
function PathFindermydis(a, b) {
    return Math.abs(a[0] - b[0]) + Math.abs(a[1] - b[1]);
}
PathFinder.prototype = {
    getNearestVert: function(p) {
        let v1 = this._options.vertices[0];
        let dis = PathFindermydis(v1, p);
        let min = this._options.vertices[0];
        this._options.vertices.forEach(function(v) {
            let d = PathFindermydis(v, p);
            if (d < dis) {
                min = v;
                dis = d;
            }
        });
        return min;
    },
    findPath: function(a, b) {
        var start = this.getNearestVert(a.geometry.coordinates);
        var finish = this.getNearestVert(b.geometry.coordinates);

        start = this._keyFn(start);
        finish = this._keyFn(finish);

        // We can't find a path if start or finish isn't in the
        // set of non-compacted vertices
        if (!this._graph.vertices[start] || !this._graph.vertices[finish]) {
            return null;
        }

        var phantomStart = this._createPhantom(start);
        var phantomEnd = this._createPhantom(finish);

        var path = findPath(this._graph.compactedVertices, start, finish);

        if (path) {
            var weight = path[0];
            path = path[1];
            return {
                path: path
                    .reduce(
                        function buildPath(cs, v, i, vs) {
                            if (i > 0) {
                                cs = cs.concat(this._graph.compactedCoordinates[vs[i - 1]][v]);
                            }

                            return cs;
                        }.bind(this),
                        []
                    )
                    .concat([this._graph.sourceVertices[finish]]),
                weight: weight,
                edgeDatas: this._graph.compactedEdges
                    ? path.reduce(
                          function buildEdgeData(eds, v, i, vs) {
                              if (i > 0) {
                                  eds.push({
                                      reducedEdge: this._graph.compactedEdges[vs[i - 1]][v]
                                  });
                              }

                              return eds;
                          }.bind(this),
                          []
                      )
                    : undefined
            };
        } else {
            return null;
        }

        this._removePhantom(phantomStart);
        this._removePhantom(phantomEnd);
    },

    serialize: function() {
        return this._graph;
    },

    _createPhantom: function(n) {
        if (this._graph.compactedVertices[n]) return null;

        var phantom = compactor.compactNode(n, this._graph.vertices, this._graph.compactedVertices, this._graph.sourceVertices, this._graph.edgeData, true, this._options);
        this._graph.compactedVertices[n] = phantom.edges;
        this._graph.compactedCoordinates[n] = phantom.coordinates;

        if (this._graph.compactedEdges) {
            this._graph.compactedEdges[n] = phantom.reducedEdges;
        }

        Object.keys(phantom.incomingEdges).forEach(
            function(neighbor) {
                this._graph.compactedVertices[neighbor][n] = phantom.incomingEdges[neighbor];
                this._graph.compactedCoordinates[neighbor][n] = phantom.incomingCoordinates[neighbor];
                if (this._graph.compactedEdges) {
                    this._graph.compactedEdges[neighbor][n] = phantom.reducedEdges[neighbor];
                }
            }.bind(this)
        );

        return n;
    },

    _removePhantom: function(n) {
        if (!n) return;

        Object.keys(this._graph.compactedVertices[n]).forEach(
            function(neighbor) {
                delete this._graph.compactedVertices[neighbor][n];
            }.bind(this)
        );
        Object.keys(this._graph.compactedCoordinates[n]).forEach(
            function(neighbor) {
                delete this._graph.compactedCoordinates[neighbor][n];
            }.bind(this)
        );
        if (this._graph.compactedEdges) {
            Object.keys(this._graph.compactedEdges[n]).forEach(
                function(neighbor) {
                    delete this._graph.compactedEdges[neighbor][n];
                }.bind(this)
            );
        }

        delete this._graph.compactedVertices[n];
        delete this._graph.compactedCoordinates[n];

        if (this._graph.compactedEdges) {
            delete this._graph.compactedEdges[n];
        }
    }
};
